package com.zzyl.nursing.service.impl;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.constant.HttpStatus;
import com.zzyl.common.core.page.TableDataInfo;
import com.zzyl.common.exception.ServiceException;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.nursing.amqp.IotMsgBody;
import com.zzyl.nursing.amqp.IotMsgNotifyData;
import com.zzyl.nursing.amqp.IotMsgService;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.dto.DeviceDataPageReqDto;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.vo.DeviceDataByDayVo;
import com.zzyl.nursing.vo.DeviceDataByWeekVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.DeviceDataMapper;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.service.IDeviceDataService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;

/**
 * 设备上报的数据Service业务层处理
 *
 * @author laoYe
 * @date 2025-10-28
 */
@Service
@Slf4j
public class DeviceDataServiceImpl extends ServiceImpl<DeviceDataMapper, DeviceData> implements IDeviceDataService {
    @Autowired
    private DeviceDataMapper deviceDataMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 查询设备上报的数据
     *
     * @param id 设备上报的数据主键
     * @return 设备上报的数据
     */
    @Override
    public DeviceData selectDeviceDataById(Long id) {
        return getById(id);
    }

    /**
     * 查询设备上报的数据列表
     *
     * @param dto 设备上报的数据
     * @return 设备上报的数据
     */
    @Override
    public TableDataInfo<DeviceData> selectDeviceDataList(DeviceDataPageReqDto dto) {
        //1. 设置分页参数
        Page<DeviceData> page = Page.of(dto.getPageNum(), dto.getPageSize());
        //2. 按上报时间降序
        page.addOrder(OrderItem.desc("alarm_time"));
        //3. 构造查询条件

        lambdaQuery().eq(StrUtil.isNotEmpty(dto.getDeviceId()), DeviceData::getIotId, dto.getDeviceId())
                .eq(StrUtil.isNotEmpty(dto.getFunctionId()), DeviceData::getFunctionId, dto.getFunctionId())
                .ge(Objects.nonNull(dto.getStartTime()), DeviceData::getAlarmTime, dto.getStartTime())
                .le(Objects.nonNull(dto.getEndTime()), DeviceData::getAlarmTime, dto.getEndTime())
                //4. 查询
                .page(page);
        //5. 封装结果返回
        return new TableDataInfo<DeviceData>(page.getTotal(), page.getRecords(), HttpStatus.SUCCESS, "查询成功");
    }

    /**
     * 新增设备上报的数据
     *
     * @param deviceData 设备上报的数据
     * @return 结果
     */
    @Override
    public int insertDeviceData(DeviceData deviceData) {
        return save(deviceData) ? 1 : 0;
    }

    /**
     * 修改设备上报的数据
     *
     * @param deviceData 设备上报的数据
     * @return 结果
     */
    @Override
    public int updateDeviceData(DeviceData deviceData) {
        return updateById(deviceData) ? 1 : 0;
    }

    /**
     * 批量删除设备上报的数据
     *
     * @param ids 需要删除的设备上报的数据主键
     * @return 结果
     */
    @Override
    public int deleteDeviceDataByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除设备上报的数据信息
     *
     * @param id 设备上报的数据主键
     * @return 结果
     */
    @Override
    public int deleteDeviceDataById(Long id) {
        return removeById(id) ? 1 : 0;
    }

    /**
     * 批量插入设备上报的数据
     *
     * @param iotMsgNotifyData
     */
    @Transactional
    @Override
    public void batchInsertDeviceData(IotMsgNotifyData iotMsgNotifyData) {
        //获取设备上报的数据
        IotMsgBody body = iotMsgNotifyData.getBody();
        if (Objects.nonNull(body)) {
            // 消息内容中的物模型数据
            List<IotMsgService> services = body.getServices();
            if (CollUtil.isNotEmpty(services)) {
                // 才需要添加到数据库中
                // 检查设备是否存在, 得到设备的iotId,在header中
                String iotId = iotMsgNotifyData.getHeader().getDeviceId();
                Device device = deviceMapper.selectOne(new LambdaQueryWrapper<Device>().eq(Device::getIotId, iotId));
                if (null != device) {
                    // 存在设备
                    // 有多少物模型属性就得遍历多次
                    List<DeviceData> dataList = new ArrayList<>();
                    services.forEach(service -> {
                        String eventTime = service.getEventTime();
                        // 上报的时间
                        LocalDateTime alarmTime = LocalDateTimeUtil.parse(eventTime, DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"))
                                .atZone(ZoneId.from(ZoneOffset.UTC))
                                .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                                .toLocalDateTime();
                        // 物模型数据
                        Map<String, Object> dataMap = service.getProperties();
                        dataMap.forEach((k, v) -> {
                            // TODO 根据物模型转成deviceData对象
                            //4条记录，4个对象, 复制对象
                            DeviceData deviceData = BeanUtil.toBean(device, DeviceData.class);
                            // 剔除创建时间、id、修改时间、更新者、创建者
                            deviceData.setId(null);
                            deviceData.setCreateTime(null);
                            deviceData.setUpdateTime(null);
                            deviceData.setUpdateBy(null);
                            deviceData.setAccessLocation(device.getRemark());
                            // functionId
                            deviceData.setFunctionId(k);
                            // datavalue
                            deviceData.setDataValue(String.valueOf(v));
                            // alarmTime
                            deviceData.setAlarmTime(alarmTime);
                            dataList.add(deviceData);
                        });
                    });
                    // 是否有需要保存的数据
                    if (CollUtil.isNotEmpty(dataList)) {
                        // 批量保存
                        saveBatch(dataList);
                        // 存入redis
                        redisTemplate.opsForHash().put(CacheConstants.IOT_DEVICE_LAST_DATA, iotId, JSONUtil.toJsonStr(dataList));
                    }
                }
            }
        }
    }

    @Override
    public List<DeviceData> selectDataByPeriod(DeviceDataPageReqDto dto) {
        return lambdaQuery().eq(DeviceData::getIotId, dto.getDeviceId())
                .eq(DeviceData::getFunctionId, dto.getFunctionId())
                .ge(null != dto.getStartTime(), DeviceData::getAlarmTime, dto.getStartTime())
                .le(dto.getEndTime() != null, DeviceData::getAlarmTime, dto.getEndTime()).list();
    }

   /* @Override
    public List<DeviceDataByDayVo> statByDay(String iotId, String functionId, Long startTime, Long endTime) {
        LocalDateTime start = LocalDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneId.systemDefault());
        LocalDateTime end = LocalDateTime.ofInstant(Instant.ofEpochMilli(endTime), ZoneId.systemDefault());

        // 查询数据
        List<DeviceData> dataList = baseMapper.selectList(new LambdaQueryWrapper<DeviceData>()
                .eq(DeviceData::getIotId, iotId)
                .eq(DeviceData::getFunctionId, functionId)
                .between(DeviceData::getCreateTime, start, end));

        // 初始化24小时数据
        List<DeviceDataByDayVo> result = new ArrayList<>(24);
        for (int i = 0; i < 24; i++) {
            DeviceDataByDayVo vo = new DeviceDataByDayVo();
            vo.setDateTime(String.format("%02d:00", i));
            vo.setDataValue(0.0);
            result.add(vo);
        }


        // 使用Map存储每小时的数据值列表
        Map<Integer, List<Double>> hourlyDataMap = new HashMap<>();
        for (int i = 0; i < 24; i++) {
            hourlyDataMap.put(i, new ArrayList<>());
        }

        // 收集每小时的数据
        dataList.forEach(data -> {
            int hour = LocalDateTime.ofInstant(data.getCreateTime().toInstant(), ZoneId.systemDefault()).getHour();
            try {
                double value = Double.parseDouble(data.getDataValue());
                hourlyDataMap.get(hour).add(value);
            } catch (NumberFormatException e) {
                // 忽略格式错误数据
            }
        });

        // 计算每小时的平均值并填充到结果中
        for (int i = 0; i < 24; i++) {
            List<Double> hourValues = hourlyDataMap.get(i);
            if (!hourValues.isEmpty()) {
                double average = hourValues.stream()
                        .mapToDouble(Double::doubleValue)
                        .average()
                        .orElse(0.0);
                result.get(i).setDataValue(average);
            }
        }

        return result;
    }*/

    @Override
    public List<DeviceDataByDayVo> statByDay(String iotId, String functionId, Long startTime, Long endTime) {
        // 将毫秒时间戳转为 LocalDateTime（系统默认时区）
        LocalDateTime start = LocalDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneId.systemDefault());
        LocalDateTime end = LocalDateTime.ofInstant(Instant.ofEpochMilli(endTime), ZoneId.systemDefault());

        // 查询该时间段内的设备数据
        List<DeviceData> dataList = baseMapper.selectList(new LambdaQueryWrapper<DeviceData>()
                .eq(DeviceData::getIotId, iotId)
                .eq(DeviceData::getFunctionId, functionId)
                .ge(DeviceData::getCreateTime, start)
                .le(DeviceData::getCreateTime, end)
        );

        // 初始化 24 小时结果（00:00 ~ 23:00）
        List<DeviceDataByDayVo> result = new ArrayList<>(24);
        for (int hour = 0; hour < 24; hour++) {
            DeviceDataByDayVo vo = new DeviceDataByDayVo();
            vo.setDataValue(0.0);
            vo.setDateTime(String.format("%02d:00", hour));
            result.add(vo);
        }

        // 按小时分组收集数值
        Map<Integer, List<Double>> hourValueMap = new HashMap<>();
        for (int i = 0; i < 24; i++) {
            hourValueMap.put(i, new ArrayList<>());
        }

        // 遍历查询结果，按小时归集
        for (DeviceData data : dataList) {
            LocalDateTime createTime = data.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
            // 确保时间在查询日内（防御性编程）
            if (createTime.isBefore(start) || createTime.isAfter(end)) {
                continue;
            }
            int hour = createTime.getHour();
            try {
                double value = Double.parseDouble(data.getDataValue());
                hourValueMap.get(hour).add(value);
            } catch (NumberFormatException e) {
                // 日志记录或忽略
                log.info("设备数据格式异常，iotId={}, value={}", iotId, data.getDataValue());
            }
        }

        // 计算每小时平均值
        for (int hour = 0; hour < 24; hour++) {
            List<Double> values = hourValueMap.get(hour);
            if (!values.isEmpty()) {
                double avg = values.stream()
                        .mapToDouble(v -> v)
                        .average()
                        .orElse(0.0);
                result.get(hour).setDataValue(avg);
            }
            // 若无数据，保持初始值 0.0
        }

        return result;
    }


    @Override
    public List<DeviceDataByWeekVo> statByWeek(String iotId, String functionId, Long startTime, Long endTime) {
        // 转换时间戳为LocalDateTime
        LocalDateTime start = LocalDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneId.systemDefault());
        LocalDateTime end = LocalDateTime.ofInstant(Instant.ofEpochMilli(endTime), ZoneId.systemDefault());

        // 查询指定时间段内的数据
        List<DeviceData> dataList = baseMapper.selectList(new LambdaQueryWrapper<DeviceData>()
                .eq(DeviceData::getIotId, iotId)
                .eq(DeviceData::getFunctionId, functionId)
                .between(DeviceData::getAlarmTime, start, end));

        // 生成时间范围内的所有日期（按天）
        List<LocalDate> dateList = new ArrayList<>();
        LocalDate currentDate = start.toLocalDate();
        LocalDate endDate = end.toLocalDate();

        while (!currentDate.isAfter(endDate)) {
            dateList.add(currentDate);
            currentDate = currentDate.plusDays(1);
        }

        // 初始化结果列表
        List<DeviceDataByWeekVo> result = new ArrayList<>(dateList.size());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM.dd");
        dateList.forEach(date -> {
            DeviceDataByWeekVo vo = new DeviceDataByWeekVo();
            vo.setDateTime(date.format(formatter));
            vo.setDataValue(0.0);
            result.add(vo);
        });

        // 填充数据值（这里取每天的最后一条数据，也可根据需求改为平均值等）
        dataList.forEach(data -> {
            LocalDate dataDate = data.getAlarmTime().toLocalDate();
            // 找到对应的日期索引
            for (int i = 0; i < dateList.size(); i++) {
                if (dateList.get(i).isEqual(dataDate)) {
                    try {
                        // 覆盖为最新值（最后一条数据）
                        result.get(i).setDataValue(Double.parseDouble(data.getDataValue()));
                    } catch (NumberFormatException e) {
                        // 数据格式异常时保持默认值0.0
                    }
                    break;
                }
            }
        });

        return result;
    }
}
